//+build usezkp

package zkp

// #cgo LDFLAGS: -L/usr/lib -lhszkp -lstdc++ -lgmp -lgomp
// #include "hszkp.h"
import "C"
import (
	"fmt"
	"unsafe"

	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/native"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda/heap"
)

func init() {
	C.hszkp_initialize()
	_zkp(hszkpInitialize, "HszkpInitialize", "()V")
	_zkp(hszkpGetHash, "HszkpGetHash", "(Ljava/lang/String;)[B")
	_zkp(hszkpHasherSetup, "HszkpHasherSetup", "()V")
	_zkp(hszkpHasherProve, "HszkpHasherProve", "(Ljava/lang/String;JLjava/lang/String;)[B")
	_zkp(hszkpHasherVerify, "HszkpHasherVerify", "([BJLjava/lang/String;JLjava/lang/String;)Z")
	_zkp(hszkpRangeSetup, "HszkpRangeSetup", "(J)V")
	_zkp(hszkpRangeProve, "HszkpRangeProve", "(JJJ)[B")
	_zkp(hszkpRangeVerify, "HszkpRangeVerify", "([BJJJ)Z")
	_zkp(hszkpMembershipSetup, "HszkpMembershipSetup", "()V")
	_zkp(hszkpMembershipProve, "HszkpMembershipProve", "(Ljava/util/ArrayList;JLjava/lang/String;J)[B")
	_zkp(hszkpMembershipVerify, "HszkpMembershipVerify", "([BJJLjava/util/ArrayList;J)Z")
}

func _zkp(method native.Method, name, desc string) {
	native.Register("com/hundsun/hsl2/basecontract/zkp/JavaSnarksJNI", name, desc, method)
}

func hszkpInitialize(frame *rtda.Frame) {
	//frame.PushLong(0)
	C.hszkp_initialize()
}
func hszkpGetHash(frame *rtda.Frame) {
	frame.PushLong(0)
}
func hszkpHasherSetup(frame *rtda.Frame) {
	//frame.PushLong(0)
	C.hszkp_hasher_setup()
}
func hszkpHasherProve(frame *rtda.Frame) {
	//frame.PushLong(0)
	hashPreimg := frame.GetLocalVar(1).Ref.JSToGoStr()
	seqId := frame.GetLocalVar(2).LongValue()
	pkFile := frame.GetLocalVar(4).Ref.JSToGoStr()
	fmt.Printf("hashPreimg = %s,seqId = %d,pkFile = %s\n", hashPreimg, seqId, pkFile)
	preimgCstr := C.CString(hashPreimg)
	pkCstr := C.CString(pkFile)
	var proofLen C.int
	var proof [256]C.char
	ret := C.hszkp_hasher_prove(unsafe.Pointer(preimgCstr), C.uint64_t(seqId), unsafe.Pointer(&proof[0]), unsafe.Pointer(&proofLen), unsafe.Pointer(pkCstr))
	fmt.Printf("prove result is %d proof length is %d proof is %v\n", ret, proofLen, proof)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	arrRef := heap.NewArray(class, uint(proofLen))
	if ret == true {
		for i := 0; i < int(proofLen); i++ {
			arrRef.GetGoBytes()[i] = byte(proof[i])
		}
	}
	frame.PushRef(arrRef)
}
func hszkpHasherVerify(frame *rtda.Frame) {
	proof := frame.GetLocalVar(1).Ref.GetGoBytes()
	proofLen := frame.GetLocalVar(2).LongValue()
	if len(proof) == 0 || proofLen == 0 {
		frame.PushBoolean(false)
		return
	}
	digestStr := frame.GetLocalVar(4).Ref.JSToGoStr()
	seqId := frame.GetLocalVar(5).LongValue()
	vkFile := frame.GetLocalVar(7).Ref.JSToGoStr()

	/*var proofC []C.char
	for i := 0; i < int(proofLen);i++ {
		proofC = append(proofC,proof[i])
	}*/
	ret := C.hszkp_hasher_verify(unsafe.Pointer(&proof[0]), C.uint64_t(proofLen), unsafe.Pointer(C.CString(digestStr)),
		C.uint64_t(seqId), unsafe.Pointer(C.CString(vkFile)))
	if ret == true {
		fmt.Println("hszkpHasherVerify ok")
	} else {
		fmt.Println("hszkpHasherVerify false")
	}
	frame.PushBoolean(bool(ret))
}
func hszkpRangeSetup(frame *rtda.Frame) {
	C.hszkp_range_setup()
}
func hszkpRangeProve(frame *rtda.Frame) {
	rangeMaxNum := frame.GetLocalVar(1).LongValue()
	inputNum := frame.GetLocalVar(3).LongValue()
	seqId := frame.GetLocalVar(5).LongValue()
	//fmt.Printf("hashPreimg = %s,seqId = %d,pkFile = %s\n",hashPreimg,seqId,pkFile)
	var proofLen C.int
	var proof [256]C.char
	ret := C.hszkp_range_prove(C.uint64_t(rangeMaxNum), C.uint64_t(inputNum), C.uint64_t(seqId), unsafe.Pointer(&proof[0]), unsafe.Pointer(&proofLen))
	fmt.Printf("prove result is %d proof length is %d proof is %v\n", ret, proofLen, proof)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	arrRef := heap.NewArray(class, uint(proofLen))
	if ret == true {
		for i := 0; i < int(proofLen); i++ {
			arrRef.GetGoBytes()[i] = byte(proof[i])
		}
	}
	frame.PushRef(arrRef)
}
func hszkpRangeVerify(frame *rtda.Frame) {
	proof := frame.GetLocalVar(1).Ref.GetGoBytes()
	proofLen := frame.GetLocalVar(2).LongValue()
	if len(proof) == 0 || proofLen == 0 {
		frame.PushBoolean(false)
		return
	}
	maxNum := frame.GetLocalVar(4).LongValue()
	seqId := frame.GetLocalVar(6).LongValue()

	ret := C.hszkp_range_verify(unsafe.Pointer(&proof[0]), C.uint64_t(proofLen), C.uint64_t(maxNum),
		C.uint64_t(seqId))
	if ret == true {
		fmt.Println("hszkpRangeVerify ok")
	} else {
		fmt.Println("hszkpRangeVerify false")
	}
	frame.PushBoolean(bool(ret))
}
func hszkpMembershipSetup(frame *rtda.Frame) {
	C.hszkp_membership_setup2()
}
func hszkpMembershipProve(frame *rtda.Frame) {
	ref := frame.GetLocalVar(1).Ref
	fields := ref.Fields.([]heap.Slot)
	setSize := fields[0].IntValue()
	setDataObj := fields[1].Ref
	var buf []byte
	for i := 0; i < int(setSize); i++ {
		elem := setDataObj.Fields.([]*heap.Object)[i].JSToGoStr()
		elemBytes := []byte(elem)
		buf = append(buf, elemBytes...)
	}
	sk := frame.GetLocalVar(4).Ref.JSToGoStr()
	seqId := frame.GetLocalVar(5).LongValue()
	var proofLen C.int
	var proof [256]C.char
	ret := C.hszkp_membership_prove2(C.CBytes(buf), C.uint64_t(setSize), unsafe.Pointer(C.CString(sk)),
		C.uint64_t(seqId), unsafe.Pointer(&proof[0]), unsafe.Pointer(&proofLen))
	fmt.Printf("prove result is %d proof length is %d proof is %v\n", ret, proofLen, proof)
	class := frame.Thread.Runtime.BootLoader().LoadClass("[B")
	arrRef := heap.NewArray(class, uint(proofLen))
	if ret == true {
		for i := 0; i < int(proofLen); i++ {
			arrRef.GetGoBytes()[i] = byte(proof[i])
		}
	}
	frame.PushRef(arrRef)
}
func hszkpMembershipVerify(frame *rtda.Frame) {
	proof := frame.GetLocalVar(1).Ref.GetGoBytes()
	proofLen := frame.GetLocalVar(2).LongValue()
	if len(proof) == 0 || proofLen == 0 {
		frame.PushBoolean(false)
		return
	}
	seqId := frame.GetLocalVar(4).LongValue()
	ref := frame.GetLocalVar(6).Ref
	fields := ref.Fields.([]heap.Slot)
	setSize := fields[0].IntValue()
	setDataObj := fields[1].Ref
	var buf []byte
	for i := 0; i < int(setSize); i++ {
		elem := setDataObj.Fields.([]*heap.Object)[i].JSToGoStr()
		elemBytes := []byte(elem)
		buf = append(buf, elemBytes...)
	}
	ret := C.hszkp_membership_verify2(unsafe.Pointer(&proof[0]), C.uint64_t(proofLen),
		C.uint64_t(seqId), C.CBytes(buf), C.uint64_t(setSize))
	if ret == true {
		fmt.Println("hszkpRangeVerify ok")
	} else {
		fmt.Println("hszkpRangeVerify false")
	}
	frame.PushBoolean(bool(ret))
}
